/** * Orweb - anonymous, secure, privacy-oriented browser for Android devices * * Based heavily upon: * Shadow - Anonymous web browser for Android devices * Copyright (C) 2009 Connell Gauld * * Thanks to University of Cambridge, * Alastair Beresford and Andrew Rice * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package info.guardianproject.browser; import fi.iki.elonen.NanoHTTPD; import fi.iki.elonen.NanoHTTPD.Response.Status; import info.guardianproject.onionkit.trust.StrongHttpsClient; import info.guardianproject.onionkit.ui.OrbotHelper; import info.guardianproject.onionkit.web.WebkitProxy; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.DownloadManager; import android.app.DownloadManager.Query; import android.app.SearchManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.PaintDrawable; import android.net.Uri; import android.net.http.SslError; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; import android.support.v7.app.ActionBarActivity; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; import android.view.Window; import android.view.WindowManager; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import android.webkit.DownloadListener; import android.webkit.SslErrorHandler; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceResponse; import android.webkit.WebSettings.PluginState; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.LinearLayout; import android.widget.Toast; import ch.boye.httpclientandroidlib.HttpEntity; import ch.boye.httpclientandroidlib.HttpHeaders; import ch.boye.httpclientandroidlib.HttpResponse; import ch.boye.httpclientandroidlib.client.methods.HttpGet; import info.guardianproject.panic.Panic; /** * The main browser activity * @author Connell Gauld * */ //public class Browser extends Activity implements UrlInterceptHandler, // OnClickListener { public class Browser extends ActionBarActivity implements OnClickListener { // TorProxy service //private ITorProxyControl mControlService = null; //private final IntentFilter torStatusFilter = new IntentFilter( // TorProxyLib.STATUS_CHANGE_INTENT); //private AnonProxy mAnonProxy = null; // UI elements private BrowserWebView mWebView = null; // private LinearLayout mNoTorLayout = null; //private LinearLayout mWebLayout = null; // private Button mStartTor = null; private LinearLayout mCookieIcon = null; // private TextView mTorStatus = null;"Error downloading" // Misc private Drawable mGenericFavicon = null; private boolean mInLoad = false; private Menu mMenu = null; private boolean mLastIsTorActive = true; private CookieDomainManager mCookieManager = null; private boolean mShowReferrer = false; private boolean mClearHistory = false; private boolean mDoJavascript = false; private boolean mShowImages = false; private OrbotHelper mOrbotHelper = null; private SharedPreferences mPrefs = null; public static String DEFAULT_PROXY_HOST = "localhost"; public static String DEFAULT_PROXY_PORT = "8118"; public static String DEFAULT_PROXY_TYPE = "HTTP"; private String mCharSet = "utf-8"; private String mLanguage = "en-US"; private Map<String,String> mHeaderOverride; private String mProxyHost = DEFAULT_PROXY_HOST; private int mProxyPort = Integer.parseInt(DEFAULT_PROXY_PORT); private String mProxyType = DEFAULT_PROXY_TYPE; public static String DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; rv:10.0) Gecko/20100101 Firefox/10.0"; public static String DEFAULT_SEARCH_ENGINE = "http://3g2upl4pq6kufc4m.onion/?q="; public static String DEFAULT_SEARCH_ENGINE_NOJS = "https://duckduckgo.com/html?q="; public static String DEFAULT_HEADER_ACCEPT = "text/html, */* ISO-8859-1,utf-8;q=0.7,*;q=0.7 gzip,deflate en-us,en;q=0.5"; private static String ABOUT_URL = "https://guardianproject.info/apps/orweb"; private Handler mLoadHandler = new Handler () { @Override public void handleMessage(Message msg) { super.handleMessage(msg); String url = msg.getData().getString("url"); if (url != null) { url = smartUrlFilter(url); mWebView.setBlockedCookies(mCookieManager.hasBlockedCookies()); mWebView.loadUrl(url, mHeaderOverride); } else if (msg.what == 1) //install Orbot { mOrbotHelper.promptToInstall(Browser.this); } else if (msg.what == 2) //start Orbot { mOrbotHelper.requestOrbotStart(Browser.this); } } }; /** private void doLoadDataOldStyle (String url) throws IOException { String mimeType = null; String encoding = null; InputStream isData = null; HttpGet hget = new HttpGet(url); initHeaders(hget); mHttpClient = getHttpClient(); mHttpClient.useProxy(true,mProxyType,mProxyHost, mProxyPort); HttpResponse response = mHttpClient.execute(hget); HttpEntity respEntity = response.getEntity(); if (response.getStatusLine().getStatusCode() == 200) { if (respEntity.getContentType() != null) { mimeType = respEntity.getContentType().getValue(); if (!mDoJavascript && mimeType.contains("javascript")) { return; } else if (!mShowImages && mimeType.contains("image")) { return; } } if (respEntity.getContentEncoding() != null) encoding = respEntity.getContentEncoding().getValue(); String mimeParts[] = mimeType.split(";"); if (mimeParts.length>1) { mimeType = mimeParts[0].trim(); mCharSet = mimeParts[1].trim().split("=")[1]; } if (encoding == null) encoding = mCharSet; isData = respEntity.getContent(); mWebView.loadDataWithBaseURL(url, removeUnproxyeableElements(isData, mCharSet), mimeType, encoding, url); } }*/ @Override public void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_PROGRESS); getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); super.onCreate(savedInstanceState); mPrefs = PreferenceManager .getDefaultSharedPreferences(getApplicationContext()); //check if Orbot is installed running mOrbotHelper = new OrbotHelper(this.getApplicationContext()); // Allow search to start by just typing setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); setContentView(R.layout.main); getSupportActionBar().setHomeAsUpIndicator(R.drawable.icon_orweb_small); getSupportActionBar().setHomeButtonEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true); mPrefs.registerOnSharedPreferenceChangeListener(new OnSharedPreferenceChangeListener () { @Override public void onSharedPreferenceChanged( SharedPreferences sharedPreferences, String key) { initSettings(); } }); initUI(); initSettings(); CacheManager.getCacheManager().setBrowser(this); setProxy(); boolean handled = handleIntent(getIntent()); if (!handled) { Message msg = new Message(); String starturl = mPrefs.getString(getString(R.string.pref_homepage), getString(R.string.default_homepage)); msg.getData().putString("url", starturl); mLoadHandler.sendMessage(msg); } } private void initUI () { // Grab UI elements mWebView = (BrowserWebView) findViewById(R.id.WebView); mWebView.setOnTouchListener(new OnTouchListener () { @Override public boolean onTouch(View v, MotionEvent event) { if (mWebView.getScrollY() > 10) Browser.this.getSupportActionBar().hide(); else Browser.this.getSupportActionBar().show(); return false; } }); //mNoTorLayout = (LinearLayout) findViewById(R.id.NoTorLayout); //mStartTor = (Button) findViewById(R.id.StartTor); mCookieIcon = (LinearLayout) findViewById(R.id.CookieIcon); //mTorStatus = (TextView) findViewById(R.id.torStatus); mCookieIcon.setOnClickListener(this); // Misc mGenericFavicon = getResources().getDrawable( R.drawable.app_web_browser_sm); // Set up UI elements //mStartTor.setOnClickListener(this); mWebView.setWebViewClient(mWebViewClient); mWebView.setWebChromeClient(mWebViewChrome); mWebView.setDownloadListener(new DownloadListener() { @Override public void onDownloadStart(final String url, String userAgent, String contentDisposition, final String mimetype, long contentLength) { final AlertDialog.Builder downloadDialog = new AlertDialog.Builder(Browser.this); downloadDialog.setTitle(info.guardianproject.browser.R.string.title_download_manager); downloadDialog.setMessage(getString(info.guardianproject.browser.R.string.prompt_would_you_like_to_download_this_file_) + '\n' + mimetype + '\n' + url); downloadDialog.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogInterface, int i) { doProxiedDownload (url, mimetype); dialogInterface.dismiss(); } }); downloadDialog.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogInterface, int i) { } }); downloadDialog.show(); } } ); mWebView.clearFormData(); mWebView.clearCache(true); mWebView.clearHistory(); } private void doProxiedDownload (String url, String mimetype) { Uri uriOriginal = Uri.parse(url); String filename = java.util.UUID.randomUUID().toString(); //generate random name String[] fileparts = uriOriginal.getEncodedPath().split("\\."); if (fileparts.length > 0) { filename += "." + fileparts[fileparts.length-1]; } String newUrl = "http://localhost:9999/" + filename + "?url=" + URLEncoder.encode(url); Uri uriDownload = Uri.parse(newUrl); boolean doStream = false; try { initDownloadManager(); if (doStream) { Intent intent = new Intent(Intent.ACTION_VIEW); //String metaMime = mimeType.substring(0,mimeType.indexOf("/")) + "/*"; intent.setDataAndType(uriDownload, mimetype); // intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); startActivity(intent); } else { doDownloadManager(uriDownload, mimetype, getString(R.string.app_name) + ": " + uriOriginal.getLastPathSegment()); } } catch (Exception e) { Log.e("Orweb","problem downloading: " + uriOriginal,e); } } private ValueCallback<Uri> mUploadMessage; private final static int FILECHOOSER_RESULTCODE = 1; @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { if (mWebView != null) mWebView.reload(); if (requestCode == FILECHOOSER_RESULTCODE) { if (null == mUploadMessage) return; Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData(); mUploadMessage.onReceiveValue(result); mUploadMessage = null; } } @SuppressLint("NewApi") public void initSettings () { if (mCookieManager == null) { CookieSyncManager.createInstance(this).stopSync(); mCookieManager = CookieDomainManager.getInstance(this); } mShowReferrer = mPrefs.getBoolean("pref_sendreferrer", false); mClearHistory = mPrefs.getBoolean("pref_clearhistory", false); mWebView.getSettings().setPluginState(PluginState.OFF); mShowImages = mPrefs.getBoolean("pref_images", true); mWebView.getSettings().setLoadsImagesAutomatically(mShowImages); mWebView.setBlockedCookiesView(mCookieIcon); String ua = mPrefs.getString("pref_user_agent", DEFAULT_USER_AGENT); mWebView.getSettings().setUserAgentString(ua); mCookieManager.setBehaviour(mPrefs.getString("pref_cookiebehaviour", "accept")); mDoJavascript = mPrefs.getBoolean(getString(R.string.pref_javascript), false); mWebView.getSettings().setJavaScriptEnabled(mDoJavascript); if (Build.VERSION.SDK_INT >= 16) { mWebView.getSettings().setAllowFileAccessFromFileURLs(false); mWebView.getSettings().setAllowUniversalAccessFromFileURLs(false); } if (Build.VERSION.SDK_INT >= 17) { mWebView.getSettings().setMediaPlaybackRequiresUserGesture(true); } mHeaderOverride = new HashMap<String,String>(); mHeaderOverride.put("User-Agent",DEFAULT_USER_AGENT); mHeaderOverride.put("Accept", DEFAULT_HEADER_ACCEPT); if (!mShowReferrer) mHeaderOverride.put("Referer",""); mHeaderOverride.put("Accept-Language", mLanguage); mHeaderOverride.put("Language", mLanguage); mHeaderOverride.put("x-requested-with",""); mHeaderOverride.put("Authentication",""); mHeaderOverride.put("Signature",""); } @Override protected void onDestroy() { unloadDownloadManager (); clearCachedData(); // mWebView.destroy(); super.onDestroy(); } public void clearCachedData () { mWebView.clearFormData(); mWebView.clearCache(true); mWebView.clearHistory(); CookieManager.getInstance().removeAllCookie(); mCookieManager.clearAllCookies(); deleteDatabase("webview.db"); deleteDatabase("webviewCache.db"); deleteDatabase("webviewCookies.db"); deleteDatabase("webviewCookiesChromium.db"); try { ArrayList<String> cmds = new ArrayList<String>(); cmds.add("rm -rf /data/data/info.guardianproject.browser/cache/webviewCacheChromium"); cmds.add("rm -rf /data/data/info.guardianproject.browser/cache/webviewCacheChromiumStaging"); cmds.add("rm -rf /data/data/info.guardianproject.browser/cache/webviewCache"); doCmds(cmds); } catch (Exception e) { Log.e("Orweb","error clearing cache data",e); } } public static void doCmds(List<String> cmds) throws Exception { Process process = Runtime.getRuntime().exec("sh"); DataOutputStream os = new DataOutputStream(process.getOutputStream()); for (String tmpCmd : cmds) { os.writeBytes(tmpCmd+"\n"); } os.writeBytes("exit\n"); os.flush(); os.close(); process.waitFor(); } /* * Set the title bar icon to the supplied bitmap. Yoinked from the Android * browser */ private void setFavicon(Bitmap icon) { Drawable[] array = new Drawable[2]; PaintDrawable p = new PaintDrawable(Color.WHITE); p.setCornerRadius(3f); array[0] = p; if (icon == null) { array[1] = mGenericFavicon; } else { array[1] = new BitmapDrawable(icon); } LayerDrawable d = new LayerDrawable(array); d.setLayerInset(1, 2, 2, 2, 2); //getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, d); getSupportActionBar().setIcon(d); } /** * Yoinked from android source * * @param url * @return */ /* private static String buildTitleUrl(String url) { String titleUrl = null; if (url != null) { try { // parse the url string URL urlObj = new URL(url); if (urlObj != null) { titleUrl = ""; String protocol = urlObj.getProtocol(); String host = urlObj.getHost(); if (host != null && 0 < host.length()) { titleUrl = host; if (protocol != null) { // if a secure site, add an "https://" prefix! if (protocol.equalsIgnoreCase("https")) { titleUrl = protocol + "://" + host; } } } } } catch (MalformedURLException e) { } } return titleUrl; }*/ static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile("(?i)" + // switch on case insensitive matching "(" + // begin group for schema "(?:http|https):\\/\\/" + "|(?:data|about|javascript):" + ")" + "(.*)"); private String smartUrlFilter(String url) { String inUrl = url.trim(); boolean hasSpace = inUrl.indexOf(' ') != -1; Matcher matcher = ACCEPTED_URI_SCHEMA.matcher(inUrl); if (matcher.matches()) { if (hasSpace) { inUrl = inUrl.replace(" ", "%20"); } // force scheme to lowercase String scheme = matcher.group(1); String lcScheme = scheme.toLowerCase(); if (!lcScheme.equals(scheme)) { return lcScheme + matcher.group(2); } return inUrl; } else if (url.indexOf(' ') != -1 || url.indexOf('.') == -1) { try { if (mDoJavascript) url = DEFAULT_SEARCH_ENGINE + URLEncoder.encode(url, mCharSet); else url = DEFAULT_SEARCH_ENGINE_NOJS + URLEncoder.encode(url, mCharSet); } catch (UnsupportedEncodingException ue) { if (mDoJavascript) url = DEFAULT_SEARCH_ENGINE + url.replace(' ','+'); else url = DEFAULT_SEARCH_ENGINE_NOJS + url.replace(' ','+'); } return url; } else return "http://" + url; } /** * Yoinked from android browser. Builds and returns the page title, which is * some combination of the page URL and title. * * @param url * The URL of the site being loaded. * @param title * The title of the site being loaded. * @return The page title. */ private String buildUrlTitle(String url, String title) { String urlTitle = url;// buildTitleUrl(url); if (title != null && title.length() > 0) { urlTitle = title + " | " + url; } return urlTitle; } @Override public boolean onOptionsItemSelected(MenuItem arg0) { Message msg = new Message(); switch (arg0.getItemId()) { case android.R.id.home: case R.id.menu_go: onSearchRequested(); return true; /* case R.id.menu_forward: mWebView.goForward(); return true; */ case R.id.menu_stop_reload: if (mInLoad) { stopLoading(); } else { mWebView.reload(); } return true; case R.id.menu_settings: startActivity(new Intent(this, EditPreferences.class)); return true; case R.id.menu_clear: clearCachedData(); return true; case R.id.menu_about: msg.getData().putString("url", ABOUT_URL); mLoadHandler.sendMessage(msg); return true; case R.id.menu_homepage: String starturl = mPrefs.getString( getString(R.string.pref_homepage), getString(R.string.default_homepage)); msg.getData().putString("url", starturl); mLoadHandler.sendMessage(msg); return true; } return super.onOptionsItemSelected(arg0); } /** * Stops the webview from loading a page */ private void stopLoading() { mInLoad = false; mWebView.stopLoading(); //mAnonProxy.stop(); setProgressBarVisibility (Boolean.FALSE); mWebViewClient.onPageFinished(mWebView, mWebView.getUrl()); } @Override public boolean onSearchRequested() { // Open up the search/go dialog startSearch(mWebView.getOriginalUrl(), true, null, false); return true; } public boolean onCreateOptionsMenu(Menu menu) { mMenu = menu; MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); getSupportActionBar().setHomeButtonEnabled(true); return true; } @Override protected void onPause() { // Registered in onResume so unregister here //unregisterReceiver(mBroadcastReceiver); //unbindService(mSvcConn); super.onPause(); } @Override protected void onResume() { super.onResume(); /* boolean updated = ((OrwebApp)getApplication()).checkLocale(); if (updated) { finish(); startActivity(getIntent()); }*/ initSettings(); } private void setProxy () { mProxyHost = mPrefs.getString("pref_proxy_host", DEFAULT_PROXY_HOST); mProxyPort = Integer.parseInt(mPrefs.getString("pref_proxy_port", DEFAULT_PROXY_PORT)); mProxyType = mPrefs.getString("pref_proxy_type", DEFAULT_PROXY_TYPE); new checkTorTask().execute(""); boolean proxyWorked = false; //enable the proxy whether Tor is running or not try { proxyWorked = WebkitProxy.setProxy("info.guardianproject.browser.OrwebApp",getApplicationContext(), mProxyHost,mProxyPort); } catch (Exception e) { Log.e("Orweb","error setting proxy",e); proxyWorked = false; } } private class checkTorTask extends AsyncTask<String, Integer, Long> { protected Long doInBackground(String... urls) { if (mProxyHost.equalsIgnoreCase("localhost") && mProxyPort == 8118) { if (!mOrbotHelper.isOrbotInstalled()) { // oh.promptToInstall(this); mLoadHandler.sendEmptyMessage(1); } else if (!mOrbotHelper.isOrbotRunning()) { // oh.requestOrbotStart(this); mLoadHandler.sendEmptyMessage(2); } } return 1l; } } private boolean isAppInstalled(String uri) { PackageManager pm = getPackageManager(); boolean installed = false; try { pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES); installed = true; } catch (PackageManager.NameNotFoundException e) { installed = false; } return installed; } private void updateTorStatus() { int status = 1; /* if (mControlService != null) { try { status = mControlService.getStatus(); } catch (RemoteException e) { // Can't do much here except leave active=false e.printStackTrace(); } } */ // Make the "Anonymous connection not available" page appear // if Tor is not active // if (status == TorProxyLib.STATUS_ON) { if (true) { //mNoTorLayout.setVisibility(View.GONE); if (mLastIsTorActive == false) mWebView.reload(); mLastIsTorActive = true; return; } //mNoTorLayout.setVisibility(View.VISIBLE); mLastIsTorActive = false; /* if (status == TorProxyLib.STATUS_CONNECTING) { mTorStatus.setText(getString(R.string.torConnecting)); return; } if (status == TorProxyLib.STATUS_REQUIRES_DEMAND) { try { mControlService.registerDemand(); mTorStatus.setText(getString(R.string.torConnecting)); return; } catch (RemoteException e) { } }*/ //mTorStatus.setText(getString(R.string.torInactive)); } public void onClick(View v) { switch (v.getId()) { case R.id.CookieIcon: CookiesBlockedDialog d = new CookiesBlockedDialog(this); d.show(); break; } } /** * Sets the title of the Activity * * @param url * the string to set the title to */ private void updateTitle(String url, String title) { getSupportActionBar().show(); setTitle(buildUrlTitle(url, title)); } /** * Sets the webview settings given a visited URL. Internal pages should show * images and run javascript even if the load images option is set to off. * * @param url * the URL of the current page */ /* private void updateSettingsPerUrl(String url) { if (url.startsWith(getString(R.string.internal_web_url))) { // This built-in home page should always show images mWebView.getSettings().setLoadsImagesAutomatically(true); mWebView.getSettings().setJavaScriptEnabled(true); } else { // Just a normal page so use the user's preference SharedPreferences mPrefs = PreferenceManager .getDefaultSharedPreferences(this); mWebView.getSettings().setLoadsImagesAutomatically( mPrefs.getBoolean(getString(R.string.pref_images), false)); mWebView.getSettings() .setJavaScriptEnabled( mPrefs.getBoolean( getString(R.string.pref_javascript), true)); String ua = mPrefs.getString("pref_user_agent", mWebView.getSettings().getUserAgentString()); mWebView.getSettings().setUserAgentString(ua); } }*/ @Override public boolean onPrepareOptionsMenu(Menu menu) { // Enable/disable the "Forward" menu item as appropriate boolean canGoForward = mWebView.canGoForward(); //menu.findItem(R.id.menu_forward).setEnabled(canGoForward); updateInLoadMenuItems(); return super.onPrepareOptionsMenu(menu); } private final WebViewClient mWebViewClient = new WebViewClient() { @Override public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) { // super.doUpdateVisitedHistory(view, url, isReload); //no history please } @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { super.onReceivedSslError(view, handler, error); String msg = "SSL error: " + error.getPrimaryError(); Log.w("SSLError",msg); Toast.makeText(Browser.this,msg,Toast.LENGTH_LONG).show(); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { //Log.i("Shadow", "Page started"); mCookieManager.clearBlockedCookies(); // Update image loading settings boolean sendCookies = mCookieManager.sendCookiesFor(url); mCookieManager.setAcceptsCookies(sendCookies); mWebView.setBlockedCookies(mCookieManager.hasBlockedCookies()); // Turn on the progress bar and set it to 10% // getWindow().requestFeature(Window.FEATURE_PROGRESS); setProgressBarVisibility (Boolean.TRUE); // setFavicon(favicon); updateInLoadMenuItems(); updateTitle(url, null); super.onPageStarted(view, url, favicon); } @Override public void onPageFinished(WebView view, String url) { updateInLoadMenuItems(); view.clearFormData(); view.clearCache(true); if (mClearHistory) view.clearHistory(); super.onPageFinished(view, url); } @Override public void onLoadResource(WebView view, String url) { mInLoad = true; //Log.d("orweb","load request: " + url); super.onLoadResource(view, url); mInLoad = false; } @SuppressLint("NewApi") @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { // Log.d("orweb","resource request: " + url); return super.shouldInterceptRequest(view, url); } public boolean shouldOverrideUrlLoading(WebView view, String url){ // handle by yourself return false; } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); Toast.makeText(Browser.this, "Error " + errorCode + ": " + description + "; " + failingUrl,Toast.LENGTH_LONG).show(); } @Override public void onTooManyRedirects(WebView view, Message cancelMsg, Message continueMsg) { super.onTooManyRedirects(view, cancelMsg, continueMsg); } /** @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { Message msg = new Message(); msg.getData().putString("url", url); mLoadHandler.sendMessage(msg); return false; } */ }; @Override protected void onNewIntent(Intent intent) { handleIntent(intent); } private boolean handleIntent(Intent intent) { if (intent != null) { String action = intent.getAction(); if (Intent.ACTION_SEARCH.equals(action)) { // Navigate to the URL String url = intent.getStringExtra(SearchManager.QUERY); Message msg = new Message(); msg.getData().putString("url", url); mLoadHandler.sendMessage(msg); return true; } else if (Intent.ACTION_VIEW.equals(action)) { // Navigate to the URL String url = intent.getDataString(); url = smartUrlFilter(url); Message msg = new Message(); msg.getData().putString("url", url); mLoadHandler.sendMessage(msg); return true; } else if (Panic.ACTION_TRIGGER.equals(action)) { clearCachedData(); Message msg = new Message(); msg.getData().putString("url", "about:blank"); mLoadHandler.sendMessage(msg); ExitActivity.exitAndRemoveFromRecentApps(this); return true; } } return false; } /** * Navigate back on the browser * * @return whether the browser navigated back */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: if (mWebView.canGoBack()) { mWebView.goBack(); return true; } break; } return super.onKeyDown(keyCode, event); } /** * Ensures that the menu items are consistent with the page loading state * (ie stop/refresh). */ private void updateInLoadMenuItems() { if (mMenu == null) { return; } MenuItem src = mInLoad ? mMenu.findItem(R.id.menu_stop) : mMenu .findItem(R.id.menu_reload); MenuItem dest = mMenu.findItem(R.id.menu_stop_reload); dest.setIcon(src.getIcon()); dest.setTitle(src.getTitle()); } private WebChromeClient mWebViewChrome = new WebChromeClient() { @Override public Bitmap getDefaultVideoPoster() { // TODO Auto-generated method stub return super.getDefaultVideoPoster(); } @Override public View getVideoLoadingProgressView() { // TODO Auto-generated method stub return super.getVideoLoadingProgressView(); } @Override public void onProgressChanged(WebView view, int newProgress) { // Update the progress bar of the activity //setSupportProgandroid:launchMode="singleTop"ress(newProgress * 100); Browser.this.setProgress(newProgress * 100); //setProgressBarIndeterminateVisibility (Boolean.TRUE); //getWindow().setFeatureInt(Window.FEATURE_PROGRESS, //newProgress * 100); if (newProgress == 100) { if (mInLoad) { mInLoad = false; updateInLoadMenuItems(); } } super.onProgressChanged(view, newProgress); } @Override public void onReceivedTitle(WebView view, String title) { updateTitle(view.getOriginalUrl(), title); super.onReceivedTitle(view, title); } // The undocumented magic method override // Eclipse will swear at you if you try to put @Override here public void openFileChooser(ValueCallback<Uri> uploadMsg) { mUploadMessage = uploadMsg; Intent i = new Intent(Intent.ACTION_GET_CONTENT); i.addCategory(Intent.CATEGORY_OPENABLE); i.setType("*/*"); Browser.this.startActivityForResult( Intent.createChooser(i, "File Browser"), FILECHOOSER_RESULTCODE); } }; @Override public void onConfigurationChanged(Configuration newConfig) { // TODO Auto-generated method stub super.onConfigurationChanged(newConfig); } protected void onSaveInstanceState(Bundle outState) { mWebView.saveState(outState); } private DownloadManager mgr; private long lastDownload = -1L; private synchronized void initDownloadManager () throws IOException { if (mgr == null) { Thread thread = new Thread () { public void run () { try { mFileServer.start(); } catch (Exception e) { Log.e("Orweb","problem starting file server",e); } } }; thread.start(); mgr=(DownloadManager)getApplicationContext().getSystemService(DOWNLOAD_SERVICE); getApplicationContext().registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); getApplicationContext().registerReceiver(onNotificationClick, new IntentFilter(DownloadManager.ACTION_NOTIFICATION_CLICKED)); } } public void showFile (String pathDownload, String mimeType) { File fileDownload = new File (pathDownload); if (!fileDownload.exists()) { fileDownload = new File(pathDownload.substring(5));// remove file: if (!fileDownload.exists()) return; } Intent newIntent = new Intent(android.content.Intent.ACTION_VIEW); newIntent.setDataAndType(Uri.fromFile(fileDownload),mimeType); newIntent.setFlags(newIntent.FLAG_ACTIVITY_NEW_TASK); try { startActivity(newIntent); } catch (android.content.ActivityNotFoundException e) { Toast.makeText(this, "No handler for this type of file.", Toast.LENGTH_LONG).show(); } } private String fileExt(String url) { if (url.indexOf("?")>-1) { url = url.substring(0,url.indexOf("?")); } if (url.lastIndexOf(".") == -1) { return null; } else { String ext = url.substring(url.lastIndexOf(".") ); if (ext.indexOf("%")>-1) { ext = ext.substring(0,ext.indexOf("%")); } if (ext.indexOf("/")>-1) { ext = ext.substring(0,ext.indexOf("/")); } return ext.toLowerCase(); } } private void unloadDownloadManager () { if (mgr != null) { mFileServer.stop(); getApplicationContext().unregisterReceiver(onComplete); getApplicationContext().unregisterReceiver(onNotificationClick); mgr = null; } } private void doDownloadManager (Uri uri, String mimeType, String title) throws IOException { lastDownload= mgr.enqueue(new DownloadManager.Request(uri) .setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE) .setAllowedOverRoaming(false) .setVisibleInDownloadsUi(true) .setMimeType(mimeType) .setShowRunningNotification(true) .setTitle(title) .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, uri.getLastPathSegment())); } BroadcastReceiver onComplete=new BroadcastReceiver() { public void onReceive(Context ctxt, Intent intent) { String action = intent.getAction(); if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) { long downloadId = intent.getLongExtra( DownloadManager.EXTRA_DOWNLOAD_ID, 0); Query query = new Query(); query.setFilterById(downloadId); Cursor c = mgr.query(query); if (c.moveToFirst()) { int columnIndex = c .getColumnIndex(DownloadManager.COLUMN_STATUS); if (DownloadManager.STATUS_SUCCESSFUL == c .getInt(columnIndex)) { final String uriString = c .getString(c .getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); final String mimetype = c .getString(c .getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE)); if (uriString != null) { final AlertDialog.Builder downloadDialog = new AlertDialog.Builder(Browser.this); downloadDialog.setTitle(info.guardianproject.browser.R.string.title_download_manager); downloadDialog.setMessage(getString(R.string.prompt_do_you_want_to_open_this_file_for_viewing_) + '\n' + mimetype + '\n' + uriString); downloadDialog.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogInterface, int i) { showFile (uriString,mimetype); dialogInterface.dismiss(); } }); downloadDialog.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogInterface, int i) { } }); downloadDialog.show(); } } } } } }; BroadcastReceiver onNotificationClick=new BroadcastReceiver() { public void onReceive(Context ctxt, Intent intent) { Intent i = new Intent(); i.setAction(DownloadManager.ACTION_VIEW_DOWNLOADS); Browser.this.startActivity(i); } }; private StrongHttpsClient mHttpClient = null; private synchronized StrongHttpsClient getHttpClient () { if (mHttpClient == null) { mHttpClient = new StrongHttpsClient(Browser.this); } mHttpClient.useProxy(true,mProxyType,mProxyHost, mProxyPort); return mHttpClient; } NanoHTTPD mFileServer = new NanoHTTPD ("localhost",9999) { @Override public Response serve(String uri, Method method, Map<String, String> header, Map<String, String> parms, Map<String, String> files) { String url = parms.get("url"); mHttpClient = getHttpClient (); HttpGet hget = new HttpGet(url); for (String key : mHeaderOverride.keySet()) hget.addHeader(key, mHeaderOverride.get(key)); try { mHttpClient.useProxy(true,mProxyType,mProxyHost, mProxyPort); HttpResponse response = mHttpClient.execute(hget); HttpEntity respEntity = response.getEntity(); String mimeType = respEntity.getContentType().getValue(); DataInputStream bis = new DataInputStream(respEntity.getContent()); Response resp = new Response(Status.OK,mimeType,bis); resp.addHeader("Content-Length", respEntity.getContentLength()+""); //resp.addHeader("Content-Type", respEntity.getContentType().getValue()); return resp; } catch (Exception e) { Log.e("Orweb","unable to proxy download",e); return new Response(Status.FORBIDDEN,"text/plain",e.getLocalizedMessage()); } } @Override public void stop() { super.stop(); } }; private String removeUnproxyeableElements (InputStream is, String encoding) throws IOException { BufferedReader r = new BufferedReader(new InputStreamReader(is,encoding)); StringBuilder total = new StringBuilder(); String line; while ((line = r.readLine()) != null) { total.append(line); } String pageData = total.toString(); pageData = pageData.replaceAll("(?i)<video[^>]*>", ""); pageData = pageData.replaceAll("(?i)<audio[^>]*>", ""); return pageData; } private void initHeaders (HttpGet hget) { String ua = mPrefs.getString("pref_user_agent", DEFAULT_USER_AGENT); hget.setHeader(HttpHeaders.USER_AGENT, ua); hget.setHeader(HttpHeaders.ACCEPT,DEFAULT_HEADER_ACCEPT); hget.setHeader(HttpHeaders.REFERER,""); hget.setHeader(HttpHeaders.ACCEPT_CHARSET,mCharSet); hget.setHeader(HttpHeaders.ACCEPT_LANGUAGE,mLanguage); } }